package io.gitee.hefren.hhconfig.server.conf;

import com.google.common.base.Throwables;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * @Date 2024/6/6
 * @Author lifei
 */
@Component
public class DistributedLock {

    private static final Logger log = LoggerFactory.getLogger(DistributedLock.class);
    @Resource
    private DataSource dataSource;

    private Connection connection;
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    private final AtomicBoolean LOCKED = new AtomicBoolean(false);

    // init
    @PostConstruct
    public void init() {
        try {
            this.connection = dataSource.getConnection();
            // 每5s 探测一下，尝试获取锁，如果能获取到锁，就将自己设置成主节点 leader
            executor.scheduleWithFixedDelay(this::tryLock, 1000, 5000, TimeUnit.MILLISECONDS);
        }catch (Exception e) {
            System.out.println(Throwables.getStackTraceAsString(e));
        }
    }

    @PreDestroy
    public void close() {
        try {
            if (Objects.nonNull(connection) && !connection.isClosed()) {
                connection.rollback();
                connection.close();
            }
        }catch (Exception e) {
            log.error("===> ignore close exception");
        }
    }

    private boolean lock() {
        try {
            // 关闭自动提供，同时这句话，也会开启一个事务
            connection.setAutoCommit(false);
            connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            connection.createStatement().execute("set innodb_lock_wait_timeout=5");
            connection.createStatement().execute("select lname from locks where lid=1 for update");
            if (LOCKED.get()) {
                log.debug("===> reentry distributed lock...");
            } else {
                log.debug("===> get distributed lock!");

            }
            return true;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void tryLock() {
        try {
            lock();
            LOCKED.set(true);
        }catch (Exception e) {
//            log.error(Throwables.getStackTraceAsString(e));
            log.error("===> not get distributed lock: {}", e.getMessage());
            LOCKED.set(false);
        }
    }

    public Boolean getLock() {
        return LOCKED.get();
    }
}
